package com.hjx.http;

import cn.hutool.core.util.ReUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.commons.text.StringEscapeUtils;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

public class FiddlerParser {

    private static Logger logger = LoggerFactory.getLogger(FiddlerParser.class);

    public static final String javaCodeTemplate = "String url%s = \"%s\";\n" +
            "String result%s = RequestUtil.%s(url%s, new HashMap<String, %s>(){{\n" +
            "%s\n" +
            "}}, new HashMap<String, String>(){{\n" +
            "%s\n" +
            "}});\n" +
            "\n" +
            "System.out.println(result%s);";

    public static final String jsCodeTemplate = "var url = \"%s\";\n" +
            "\n" +
            "var params = new LinkedHashMap();\n" +
            "with(Object.bindProperties({}, params)) {\n" +
            "%s\n" +
            "}\n" +
            "var headers = new LinkedHashMap();\n" +
            "with(Object.bindProperties({}, headers)) {\n" +
            "%s\n" +
            "}\n" +
            "var result%s = RequestUtil.%s(url, params, headers);\n" +
            "println(result%s)";

    public static final String requestConfigTemplate = "var requestConfig = {\n" +
            "    name : \"%s\", \n" +
            "    url : \"%s\",\n" +
            "    method : \"%s\",\n" +
            "    queryString : \"%s\", \n" +
            "    headers : %s,\n" +
            "    requestDataFormat : \"%s\"\n" +
            "    body : %s,\n" +
            "    reponseAs : \"%s\",\n" +
            "    success : function(response){\n" +
            "        logger.info(res(response)\n" +
            "    }\n" +
            "\n" +
            "}";


    private String fromPath = "";
    private String toPath = "";
    @Before
    public void setUp(){
//        fromPath = "C:\\Users\\Administrator\\Desktop\\request.saz";
//        toPath = "C:\\Users\\Administrator\\Desktop\\requestZip";
//        fromPath = "C:\\Users\\Administrator\\Desktop\\beijingDX.saz";
//        toPath = "C:\\Users\\Administrator\\Desktop\\beijingDXZip";
//        fromPath = "C:\\Users\\Administrator\\Desktop\\reply.saz";
//        toPath = "C:\\Users\\Administrator\\Desktop\\replyZip";
//        fromPath = "C:\\Users\\Administrator\\Desktop\\deletePost.saz";
//        toPath = "C:\\Users\\Administrator\\Desktop\\deletePostZip";
        fromPath = "C:\\Users\\Administrator\\Desktop\\login.saz";
        toPath = "C:\\Users\\Administrator\\Desktop\\loginZip";
    }

    @Test
    public void unzipSAZ() throws IOException {

        decompression(fromPath, toPath);

    }

    @Test
    public void parseSAZ() throws IOException {
        Map<Integer, String> requestSeq = new HashMap<Integer, String>();

        Path from = Paths.get(toPath + "/raw");
        Files.walkFileTree(from, new SimpleFileVisitor<Path>(){

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                String fileName = file.getFileName().toString();
                if (fileName.endsWith("xml")){
                    return super.visitFile(file, attrs);
                } else {
                    if(fileName.contains("c")){
                        String[] fileNameTokens = fileName.split("_");
                        try {
                            requestSeq.put(Integer.valueOf(fileNameTokens[0]), convertHttpRequest2JavaCode(fileNameTokens, Files.readAllLines(file)));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }


                    } else if(fileName.contains("s")){
//                        System.out.println();
                    } else {
                        System.out.println(file);
                    }
                    return super.visitFile(file, attrs);
                }

            }
        });

        requestSeq.forEach((k,v) -> {
//            logger.info("{} -> \n{}", k, v);
            logger.info(v.trim());
        });
    }



    public static String convertHttpRequest2JavaCode(String[] fileNameTokens, List<String> rawRequest){
        AtomicReference<String> method = new AtomicReference<>("");
        AtomicReference<String> url = new AtomicReference<>("");
        StringBuilder javaSB = new StringBuilder();
        StringBuilder jsSB = new StringBuilder();

        String body = "";
        List<String> excludeLastList;
        if (!rawRequest.get(rawRequest.size()-1).equals("")) {
            excludeLastList = new ArrayList<String>(){{
                addAll(rawRequest);
            }};
            body = excludeLastList.remove(excludeLastList.size()-1);
        } else {
            excludeLastList = rawRequest;
        }


        excludeLastList.stream().distinct().forEach(line -> {
            String regex = "(GET|POST)\\s(.*)\\s(HTTP/1.1)";
            if(line.matches(regex)) {
                method.set(ReUtil.findAll(regex, line, 1).get(0).toLowerCase());
                url.set(ReUtil.findAll(regex, line, 2).get(0));
            } else if(!line.equals("") && !line.contains("Content-Length")){
                javaSB.append("\t").append(StringEscapeUtils.escapeJava(line).replaceAll(": ", "\", \"").replaceAll(".*\", \".*", "put(\"$0\");\n"));
                jsSB.append("\t\t").append(StringEscapeUtils.escapeJava(line).replaceAll(": ", "\": \"").replaceAll(".*\": \".*", "\"$0\",\n"));
            }
        });

        boolean bodyIsJson = isJSON(body);
        String[] urlTokens = url.get().split("\\?");
        logger.info("{}", String.format(requestConfigTemplate,
                urlTokens[0].substring(urlTokens[0].lastIndexOf("/") + 1).replaceAll("\\.", ""),
                urlTokens[0],
                method.get(),
                urlTokens.length > 1 ? urlTokens[1] : "",
                jsSB.insert(0, "{\n" ).deleteCharAt(jsSB.length()-2).append("\t}"),
                bodyIsJson ? "json" : "form",
                convertRequestBody2JSCode(body),
                ""));
        return String.format(javaCodeTemplate, fileNameTokens[0], url.get(), fileNameTokens[0], bodyIsJson ? "postJson" : method.get(), fileNameTokens[0], isJSON(body) ? "Object" : "String" , convertRequestBody2JavaCode(body), javaSB.deleteCharAt(jsSB.length()-1).toString(), fileNameTokens[0]);
    }

    private static String convertRequestBody2JSCode(String body) {


        if(isJSON(body)){
            return prettyFormatJsonString(body);
        } else {
            StringBuilder sb = new StringBuilder();
            String[] params = body.split("&");
            Arrays.stream(params).forEach(keyValue -> {
                String[] keyAndValue = keyValue.split("=");
                sb.append("\t\t").append("\"").append(keyAndValue[0]).append("\" : \"").append(keyAndValue.length == 1 ? "" : keyAndValue[1]).append("\",\n");
            });
            return sb.insert(0, "{\n" ).deleteCharAt(sb.length()-2).append("\t}").toString();
        }
    }

    private static String convertRequestBody2JavaCode(String body) {

        StringBuilder sb = new StringBuilder();
        if(isJSON(body)){
            sb.append("\t").append("String body = \"").append(StringEscapeUtils.escapeJava(body)).append("\";\n");
            sb.append("\t").append("putAll(JSON.parseObject(body, Map.class));");
            return sb.toString();
        } else {

            String[] params = body.split("&");
            Arrays.stream(params).forEach(keyValue -> {
                String[] keyAndValue = keyValue.split("=");
                sb.append("\t").append("put(\"").append(keyAndValue[0]).append("\", \"").append(keyAndValue.length == 1 ? "" : keyAndValue[1]).append("\");\n");
            });
            return sb.deleteCharAt(sb.length()-1).toString();
        }
    }

    public static boolean isJSON(String body) {
        try {

            JSON.parseObject(body);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }


    /**
     * 解压SAZ包
     * @param fromPathStr
     * @param targetPathStr
     * @throws IOException
     */
    public static void decompression(String fromPathStr, String targetPathStr) throws IOException {
        Path fromPath = Paths.get(fromPathStr);
        final Path targetPath = Paths.get(targetPathStr);
        Files.createDirectories(targetPath);
        FileSystem fs = FileSystems.newFileSystem(fromPath, null);
        long startTime = System.currentTimeMillis();
        Files.walkFileTree(fs.getPath("/"), new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                Files.copy(file, targetPath.resolve(file.toString().substring(1)), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                if (dir.getParent() != null) {
                    if (dir.getFileName().toString().equals("__MACOSX/")) { // MAC系统压缩自带的隐藏文件过滤
                        return FileVisitResult.SKIP_SUBTREE;
                    }
                    Files.createDirectories(targetPath.resolve(dir.toString().substring(1)));
                }

                return FileVisitResult.CONTINUE;
            }
        });
        long endTime = System.currentTimeMillis();
        logger.info("解压文件花费时间:{}", endTime - startTime + " ms");
    }

    public static String prettyFormatJsonString(String body){
        JSONObject object = JSON.parseObject(body);
        return JSON.toJSONString(object, SerializerFeature.PrettyFormat);
    }
}
